/**
 *    Copyright (C) 2008  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_MAP_KV_PAGE_TYPES_HPP
#define	_CUBEFS_CORE_MAP_KV_PAGE_TYPES_HPP

#include <cubefs/core/types.hpp>

#include <cubefs/core/btree/page_types.hpp>
#include <cubefs/core/btree/pages.hpp>

#include <cubefs/core/tools/types/typelist.hpp>
#include <cubefs/core/tools/types/node_dispatcher.hpp>

namespace cubefs    {
namespace map       {
namespace kv        {

using namespace cubefs::tools;
using namespace cubefs::btree;

template <
        typename Key,
        typename Value,
        PageDescriptor PageDescriptorCodeValue,
        typename PageMgr,
        int Indexes                                         = 1,
        template <typename PageMgr> class Metadata          = BTreeMetadata,
        template <typename Type>    class CountersType      = BTreeCountersBase,
        typename NodeComponentList                          = TypeList<None, NullType>,
        typename RootComponentList                          = TypeList<RootNodeBase<PageMgr, Metadata>, NullType>,
        
        typename TreePage                                   = TreePage<PageMgr>,
        
        template <
            typename PageMgr,
            typename Key,
            typename Value,
            template <typename Type> class CountersType_,
            typename ComponentList_,
            typename Base_,
            int Indexes_
        > class NodePage                                    = NodePage
    >

class PageTypes: public PageTypesBase<PageMgr, PageDescriptorCodeValue, Key, Value> {
    typedef PageTypesBase<PageMgr, PageDescriptorCodeValue, Key, Value> base;

    typedef NodePageBase<PageMgr, CountersType, TreePage>       PageBase;
    
public:
    typedef typename PageMgr::Page::ID                          ID;

    typedef NodePage<
                PageMgr,
                Key,
                ID,
                CountersType,
                NodeComponentList,
                TreePage,
                Indexes>                                        InternalNode;

    typedef NodePage<
                PageMgr,
                Key,
                Value,
                CountersType,
                NodeComponentList,
                TreePage,
                Indexes>                                        LeafNode;

    typedef NodePage<
                PageMgr,
                Key,
                ID,
                CountersType,
                typename AppendTool<
                    RootComponentList,
                    NodeComponentList>::Result, TreePage, Indexes> RootNode;

    typedef NodePage<
                PageMgr,
                Key,
                Value,
                CountersType,
                typename AppendTool<
                    RootComponentList,
                    NodeComponentList>::Result, TreePage, Indexes> RootLeafNode;


    typedef PageBase                                            NodeBase;
    typedef CountersType<BigInt>                                Counters;

    static const long IndexesPerPage                            = Indexes;

private:

    typedef typename TypeListTool<
            TypedNode<0, InternalNode>,
            TypedNode<1, LeafNode>,
            TypedNode<2, RootNode>,
            TypedNode<3, RootLeafNode> >::List                                  NodeTypes;

    typedef typename TypeListTool<
            TypedNode<0, RootNode>,
            TypedNode<1, RootLeafNode> >::List                                  RootTypes;
    
    typedef typename TypeListTool<
            TypedNode<0, LeafNode>,
            TypedNode<1, RootLeafNode> >::List                                  LeafTypes;

    typedef typename TypeListTool<
            TypedNode<0, InternalNode>,
            TypedNode<1, RootNode> >::List                                      InodeTypes;

public:
    typedef typename TypeListTool<
            Pair<InternalNode, RootNode>,
            Pair<LeafNode, RootLeafNode>,
            Pair<RootNode, RootNode>,
            Pair<RootLeafNode, RootLeafNode> >::List                            Node2RootMap;

    

    typedef typename TypeListTool<
            Pair<RootNode, InternalNode>,
            Pair<RootLeafNode, LeafNode>,
            Pair<InternalNode,  InternalNode>,
            Pair<LeafNode, LeafNode> >::List                                    Root2NodeMap;

private:
    template <typename Base>
    struct NodeDispatcherBase {
        static int DispatchType(Base *page) {
            return page->is_root() * 2 + page->is_leaf();
        }
    };
    
    template <typename Base>
    struct RootDispatcherBase {
        static int DispatchType(Base *page) {
            return page->is_leaf();
        }
    };

    template <typename Base>
    struct LeafDispatcherBase {
        static int DispatchType(Base *page) {
            return page->is_root();
        }
    };

    template <typename Base>
    struct InodeDispatcherBase {
        static int DispatchType(Base *page) {
            return page->is_root();
        }
    };
    
public:
    typedef cubefs::core::tools::types::nodedispatcher::NodeDispatcher<NodeBase, NodeDispatcherBase,  NodeTypes>        NodeDispatcher;
    typedef cubefs::core::tools::types::nodedispatcher::NodeDispatcher<NodeBase, RootDispatcherBase,  RootTypes>        RootDispatcher;
    typedef cubefs::core::tools::types::nodedispatcher::NodeDispatcher<NodeBase, LeafDispatcherBase,  LeafTypes>        LeafDispatcher;
    typedef cubefs::core::tools::types::nodedispatcher::NodeDispatcher<NodeBase, InodeDispatcherBase, InodeTypes>       InternalNodeDispatcher;


    typedef PageTypeDescriptor<base::PageDescriptorCode, NodeDispatcher, NodeBase>                        PageDispatcher;

    static NodeBase *CreateNode(PageMgr *mgr, typename PageMgr::Transaction &txn, Short level, bool root)
    {
        NodeBase *node;
        if (level > 0)
        {
            if (root)
            {
                RootNode *_node = new (mgr, txn) RootNode;
                node = _node;
            }
            else
            {
                InternalNode *_node = new (mgr, txn) InternalNode;
                node = _node;
            }

            node->set_leaf(false);
        }
        else
        {
            if (root)
            {
                RootLeafNode *_node = new (mgr, txn) RootLeafNode;
                node = _node;
            }
            else
            {
                LeafNode *_node = new (mgr, txn) LeafNode;
                node = _node;
            }

            node->set_leaf(true);
            node->counters().page_count() = 1;
        }

        node->flags().page_type() = base::PageDescriptorCode;

        node->level() = level;
        return node;
    }
};

}
}
}

#endif //_CUBEFS_CORE_MAP_KV_PAGE_TYPES_HPP